////////////////////////////////////////////////////////////////////////////////

#ifndef _CLIENTBATTLEFIELD_H_
#include "clientbattlefield.h"
#endif

////////////////////////////////////////////////////////////////////////////////

#ifndef _SERVERSTATE_H_
#include "serverstate.h"
#endif

#include <cassert>
#include <iostream>

////////////////////////////////////////////////////////////////////////////////


ClientBattlefield::ClientBattlefield()
{
}


////////////////////////////////////////////////////////////////////////////////


void ClientBattlefield::Integrate( const ServerState& _firstState,
                                   const ServerState& _secondState,
                                   double _firstToSecondCoeff )
{
    IntegratePositions( GetPlayers(),
                        _firstState.GetPlayers(),
                        _secondState.GetPlayers(),
                        _firstToSecondCoeff );

    IntegratePositions( GetBullets(),
                        _firstState.GetBullets(),
                        _secondState.GetBullets(),
                        _firstToSecondCoeff );
}


////////////////////////////////////////////////////////////////////////////////


template <typename _CollType>
void ClientBattlefield::IntegratePositions( _CollType& _currStateObjects,
                                            const _CollType& _firstStateObjects,
                                            const _CollType& _secondStateObjects,
                                            double _firstToSecondCoeff )
{
    // Begin analyzing

    // If we have no objects in first state - we have no objects in current
    if ( _firstStateObjects.empty() )
    {
        _currStateObjects.clear();
        return;
    }

    // Synchronize first and current state
    SynchronizeFirstAndCurrentState(_currStateObjects, _firstStateObjects);

    // If we have no objects in second state - we still have them in current
    if ( _secondStateObjects.empty() )
    {
        return;
    }



    // We are here if we have objects in both states
    typename _CollType::iterator currStateObjIt = _currStateObjects.begin();
    typename _CollType::const_iterator firstStateObjIt = _firstStateObjects.begin();
    typename _CollType::const_iterator secondStateObjIt = _secondStateObjects.begin();

    // While we have valid iterators for first and second states
    while ( currStateObjIt != _currStateObjects.end() &&
            firstStateObjIt != _firstStateObjects.end() &&
            secondStateObjIt != _secondStateObjects.end() )
    {
        // Get object from the second state
        const GameObject& secondStateObject = *secondStateObjIt;

        // Get object from the first state
        const GameObject& firstStateObject = *firstStateObjIt;

        // Get object from the current state
        GameObject& currStateObject = *currStateObjIt;

        // If it is the same player
        if ( secondStateObject.GetID() == firstStateObject.GetID() )
        {
            IntegrateObjectPosition( currStateObject,
                                     firstStateObject,
                                     secondStateObject,
                                     _firstToSecondCoeff );

            // Take next player from first state
            ++firstStateObjIt;
            ++currStateObjIt;
        }
        else if ( secondStateObject.GetID() > firstStateObject.GetID() )
        {
            // Leaver
            // Do not delete him from current state,
            // because it will be deleted during next synchronization
            ++firstStateObjIt;
            continue;
        }
        else
        {
            // Should never reach here, because we could not have new objects
            // with ID less than old objects
            assert(0);
        }

        ++secondStateObjIt;
    }
}


////////////////////////////////////////////////////////////////////////////////


template <typename _CollType>
void ClientBattlefield::SynchronizeFirstAndCurrentState( _CollType& _currStateObjects,
                                                         const _CollType& _firstStateObjects )
{
    // If there is no objects in current state - just copy them
    if ( _currStateObjects.empty() )
    {
        _currStateObjects = _firstStateObjects;
        return;
    }

    // Go through all first-state objects
    typename _CollType::iterator currStateObjIt = _currStateObjects.begin();
    typename _CollType::const_iterator firstStateObjIt = _firstStateObjects.begin();
    while ( currStateObjIt != _currStateObjects.end() &&
            firstStateObjIt != _firstStateObjects.end() )
    {
        const GameObject& firstStateObject = *firstStateObjIt;
        const GameObject& currStateObject = *currStateObjIt;

        // Compare objects IDs
        if ( firstStateObject.GetID() > currStateObject.GetID() )
        {
            // We have a leaver (leaver with low ID)
            currStateObjIt = _currStateObjects.erase(currStateObjIt);
        }
        else if ( firstStateObject.GetID() == currStateObject.GetID() )
        {
            // Objects match
            // Synchronize next two objects
            ++firstStateObjIt;
            ++currStateObjIt;
        }
        else
        {
            // Should never reach here, because we could not have new objects
            // with ID less than old objects
            assert(0);
        }


        // If we have finished checking all objects in current state - just copy new objects and exit
        if ( currStateObjIt == _currStateObjects.end() )
        {
            _currStateObjects.insert( currStateObjIt, firstStateObjIt, _firstStateObjects.end() );
            return;
        }
    }

    // Leavers (with high ID)
    while ( currStateObjIt != _currStateObjects.end() )
        currStateObjIt = _currStateObjects.erase(currStateObjIt);
}


////////////////////////////////////////////////////////////////////////////////


void ClientBattlefield::IntegrateObjectPosition( GameObject& _current,
                                                 const GameObject& _first,
                                                 const GameObject& _second,
                                                 double _firstToSecondCoeff )
{
    Point position;

    position.x = static_cast<float>( _first.GetPosition().x * _firstToSecondCoeff +
                                     _second.GetPosition().x * (1.0 - _firstToSecondCoeff) );

    position.y = static_cast<float>( _first.GetPosition().y * _firstToSecondCoeff +
                                     _second.GetPosition().y * (1.0 - _firstToSecondCoeff) );

    _current.SetPosition(position);
}



////////////////////////////////////////////////////////////////////////////////
